﻿using System;
using System.Collections.Generic;

using System.Text;
using EasyHook;
using System.Runtime.Remoting;
using System.Windows.Forms;
using Inject;
using System.Diagnostics;
using System.Drawing.Win32;
using Layers;
using HoldemGame;

namespace Holdem.Inject
{
    public class StringEventArgs : EventArgs
    {
        public DateTime InTime;
        public string Message;

        public StringEventArgs(string msg, DateTime dt)
        {
            Message = msg;
            InTime = dt;
        }
    }

    public class FTChat : WinWnd
    {
        public DateTime LastMessageTime;
        public string LastMessage;

        private EventHandler<StringEventArgs> _newMsg;


        public event EventHandler<StringEventArgs> NewMessage
        {
            add { _newMsg += value; }
            remove { _newMsg -= value; }
        }

        public FTChat(WinWnd wnd)
            : base(wnd)
        {
            ListenCalls();
        }

        public override void OnWindowLinked(WinWnd other)
        {
            if (null == other)
            {
                FTTable tbl = ParentOfType<FTTable>(this);
                if (null != tbl)
                {
                    tbl.Chat = this;
                    FTDriver.NotifyGameOpened(tbl);
                }
            }
        }
        protected override void OnExtTextOut(int X, int Y, string msg, CallEventArgs e)
        {
            IntPtr hdc = (IntPtr)e.Args[1];
            IntPtr hdcWnd = (IntPtr)e.Args[0];
            
            if (hdcWnd != IntPtr.Zero && hdcWnd != this.Handle)
                return; // noy my target window!



            if (e.InTime.CompareTo(LastMessageTime) > 0)
            {
                if (msg.StartsWith("Dealer:"))
                {
                    Api.POINT pt = new Api.POINT();
                    Api.GetDCOrgEx(hdc, ref pt);
                    IntPtr myDC = Api.GetDC(this.Handle);
                    MyLogger.WriteLine("X={0} Y={1} T='{2}' WND '{3}' LOC {4} WND={5} DC2WND={7} DC={6} myDC={8}", X, Y, msg, Text, this.Rect, Handle, hdc, hdcWnd, myDC);
                    Api.ReleaseDC(this.Handle, myDC);
                    LastMessageTime = e.InTime;
                    LastMessage = msg;
                    if (null != _newMsg)
                        _newMsg(this, new StringEventArgs(LastMessage, LastMessageTime));
                }
                else
                {
                }
            }
        }
    }

    public class FTPlayer
    {
        public string ID;
        public double Stake;
    }

    public class FTTable : WinWnd
    {
        public WinWnd TableWnd;
        public DateTime LastMessageTime;
        public string LastMessage;
        public FTChat FTChat;

        public List<FTPlayer> Players = new List<FTPlayer>();
        public double Pot;

        public Mosaic Mosaic = new Mosaic(0,0);

        private EventHandler<StringEventArgs> _newMsg;

        public FTChat Chat = null;

        string totalPot = "Total Pot: $";

        List<string> skip = new List<string>(new string[]{
            "SMALL BLIND","BET","CHECK","FOLD","RAISE","BIG BLIND","Reserved","CALL", "ALL IN"});


        public event EventHandler<StringEventArgs> NewMessage
        {
            add { _newMsg += value; }
            remove { _newMsg -= value; }
        }

        public FTTable(WinWnd wnd) : base(wnd)
        {
            Mosaic = new Mosaic(wnd.Rect.Width / 7, wnd.Rect.Height / 16 / 2);

            ListenCalls();
        }



        double ParseDouble(string msg)
        {
            return 2.0;
        }

        void TotalPot(double totPot)
        {
            if (Pot != totPot)
            {
                Pot = totPot;
                PotChanged(totPot);
            }
        }

        void PotChanged(double totPot)
        {
            MyLogger.WriteLine("---> NEW POT: {0}", totPot);
        }
        static int nn = 0;
        protected override void OnExtTextOut(int X, int Y, string msg, CallEventArgs e)
        {
            IntPtr hwnd=(IntPtr)e.Args[0];
            if (hwnd == IntPtr.Zero)
            {
                if (msg.StartsWith(totalPot))
                    TotalPot(ParseDouble(msg.Substring(totalPot.Length)));
                else if (msg.StartsWith("$"))
                    Mosaic.Add(X, Y, msg, (uint)TileClass.Money);
                else if(!skip.Contains(msg) && msg.Trim().Length>0)
                    Mosaic.Add(X, Y, msg, 0);
                Mosaic.X0 = this.Rect.Width / 2;
                Mosaic.Y0 = this.Rect.Height / 2;
                Mosaic.CalcAngles(Mosaic.X0, Mosaic.Y0);
                Mosaic.Sort();
                if (nn++% 10 == 0)
                {
                    //MyLogger.WriteLine("MOSAIC X={0} Y={1} T='{2}' hDC={3} hWnd={4} thisWnd={5}", X, Y, msg, e.Args[1], e.Args[0], this.ClassName+":"+this.Handle);
                    MyLogger.WriteLine("MOSAIC({0}):{1}\n", Mosaic.Tiles.Count, Mosaic);
                }
            }
        }
    }


    public class FTMessageProxy
    {
        FTChat _chat;
        FTTable _table;

        IGame _target;

        string prefix = "Dealer: ";
        string postbb = " posts the big blind of ";
        string postsb = " posts the small blind of ";
        string handno = "Hand #";
        string timeleft = " seconds left to act";
        string posts = " posts ";
        string mycards = "You have been dealt [";
        string checks = " checks";
        string raisesto = " raises to ";
        string calls = " calls ";
        string folds = " folds";
        string turn = "The turn is [";
        string river = "The river is [";
        string flop = "The flop is [";
        string bets = " bets ";
        string adds = " adds ";
        string shows = " shows ";
        string winsmain = " wins the main pot (";
        string wins = " wins the pot (";
        string winswith = ") with ";
        string sitout = " is sitting out";
        string reconnect = " has reconnected";
        string ties = " ties for the pot (";
        string disconnect = " has been disconnected";
        string mucks = " mucks";
        string standup = " stands up";
        string secleft = " seconds left to act";
        string has = " has ";
        string rcp = ")";
        string rsp = "]";
        string uncalledbet="Uncalled bet of ";
        string returnedto = " returned to ";
        string sitdown = " sits down";
        
        int position = 0;
        long handId = 0;
        int step = -1;

        string MyName = null;

        CardSet _boardCards = new CardSet();
        //string hasreturned = " has returned";
        public enum Post : uint
        {
            Money = 0,
            SmallBlind = 1,
            BigBlind = 2
        }

        public FTMessageProxy(FTTable table, IGame target)
        {
            _chat = table.Chat;
            _target = target;
            _table = table;
            _chat.NewMessage += new EventHandler<StringEventArgs>(OnNewMessage);
        }

        void OnNewMessage(object sender, StringEventArgs e)
        {
            Parse(e.Message);
        }

        double ParseDouble(string msg)
        {
            string msg2 = msg.Trim('$', ' ', ',');
            string msg3 = msg2.Replace(",", "");
            if (msg3 != msg)
                MyLogger.WriteLine("PARSE: [{0}]->[{1}]", msg, msg3);
            try
            {
                
                return double.Parse(msg3);
            }
            catch (Exception e)
            {
                MyLogger.WriteLine("ERR: Failed to Parse Double [{0}]",msg3);
                return double.NaN;
            }
        }

        void Parse(string msg)
        {
            if(!msg.StartsWith(prefix))
                throw new InvalidOperationException(string.Format("Message {0} doesn't start with {1} prefix", msg, prefix));
            try
            {
                msg = msg.Substring(prefix.Length);

                int i, j;

                i = msg.IndexOf(handno);
                if (i >= 0)
                {
                    HandStart(long.Parse(msg.Substring(i + handno.Length)));
                    return;
                }

                i = msg.IndexOf(postbb);
                if (i >= 0)
                {
                    Blind(msg.Substring(0, i), ParseDouble(msg.Substring(i + postbb.Length)), Post.BigBlind);
                    return;
                }

                i = msg.IndexOf(postsb);
                if (i >= 0)
                {
                    Blind(msg.Substring(0, i), ParseDouble(msg.Substring(i + postsb.Length)), Post.SmallBlind);
                    return;
                }

                i = msg.IndexOf(timeleft);
                if (i >= 0)
                {
                    //bobeobi has 15 seconds left to act
                    if (msg[0] >= '0' && msg[0] <= '9')
                    {
                        TimeLeft(null, ParseDouble(msg.Substring(0, i)));
                        return;
                    }
                    else
                    {
                        j = msg.IndexOf(has);
                        int k = msg.IndexOf(secleft);

                        if (j >= 0)
                        {
                            if (k >= 0)
                            {
                                TimeLeft(msg.Substring(0, j), ParseDouble(msg.Substring(j + has.Length, k - j - has.Length)));
                                return;
                            }
                        }
                        else
                        {
                            TimeLeft(null, ParseDouble(msg.Substring(0, i)));
                            return;
                        }
                    }
                }

                i = msg.IndexOf(posts);
                if (i >= 0)
                {
                    Blind(msg.Substring(0, i), ParseDouble(msg.Substring(i + posts.Length)), Post.Money);
                    return;
                }

                i = msg.IndexOf(mycards);
                if (i >= 0)
                {
                    j = msg.IndexOf(rsp);
                    if (j >= 0)
                    {
                        MyCards(msg.Substring(i,j-i));
                        return;
                    }
                }

                i = msg.IndexOf(checks);
                if (i >= 0)
                {
                    Check(msg.Substring(0, i));
                    return;
                }

                i = msg.IndexOf(raisesto);
                if (i >= 0)
                {
                    RaiseTo(msg.Substring(0, i), ParseDouble(msg.Substring(i + raisesto.Length)));
                    return;
                }

                i = msg.IndexOf(calls);
                if (i >= 0)
                {
                    Call(msg.Substring(0, i), ParseDouble(msg.Substring(i + calls.Length)));
                    return;
                }

                i = msg.IndexOf(bets);
                if (i >= 0)
                {
                    Bet(msg.Substring(0, i), ParseDouble(msg.Substring(i + bets.Length)));
                    return;
                }

                i = msg.IndexOf(adds);
                if(i>=0)
                {
                    BetAdd(msg.Substring(0,i),ParseDouble(msg.Substring(i+adds.Length)));
                    return;
                }

                i = msg.IndexOf(uncalledbet);
                if (i >= 0)
                {
                    j=msg.IndexOf(returnedto);
                    if(j>=i)
                    {
                        BetReturn(msg.Substring(j+returnedto.Length),
                            ParseDouble(msg.Substring(i + uncalledbet.Length, j - i - uncalledbet.Length)));
                        return;
                    }
                }
                i = msg.IndexOf(folds);
                if (i >= 0)
                {
                    Fold(msg.Substring(0, i));
                    return;
                }

                i = msg.IndexOf(flop);
                if (i >= 0)
                {
                     j = msg.IndexOf(rsp);
                     if (j >= 0)
                     {
                         Flop(msg.Substring(i + flop.Length, j - i - flop.Length));
                         return;
                     }
                }

                i = msg.IndexOf(turn);
                if (i >= 0)
                {
                     j = msg.IndexOf(rsp);
                     if (j >= 0)
                     {
                         Turn(msg.Substring(i + turn.Length, j - i - turn.Length));
                         return;
                     }
                }
                
                i = msg.IndexOf(river);
                if (i >= 0)
                {
                     j = msg.IndexOf(rsp);
                     if (j >= 0)
                     {
                         River(msg.Substring(i + river.Length,j-i-river.Length));
                         return;
                     }
                }
                
                i = msg.IndexOf(shows);
                if (i >= 0)
                {
                     j = msg.IndexOf(rsp);
                     if (j >= 0)
                     {
                         Shows(msg.Substring(0, i), msg.Substring(i + shows.Length, j - i - shows.Length));
                         return;
                     }
                     else
                     {
                         Shows(msg.Substring(0, i), msg.Substring(i + shows.Length));
                         return;
                     }
                }
                
                i = msg.IndexOf(wins);
                if (i >= 0)
                {
                    j = msg.IndexOf(winswith);

                    string comb = null;
                    if (j >= 0)
                        comb = msg.Substring(j + winswith.Length);
                    int k = msg.IndexOf(rcp);
                    if (k >= i + wins.Length)
                    {
                        Wins(msg.Substring(0, i),
                            ParseDouble(msg.Substring(i + wins.Length, k - i - wins.Length)), comb);
                        return;
                    }
                }

                i = msg.IndexOf(winsmain);
                if (i >= 0)
                {
                    j = msg.IndexOf(winswith);
                    string comb = null;
                    if (j >= 0)
                        comb = msg.Substring(j + winswith.Length);
                    int k = msg.IndexOf(rcp);
                    if (k >= i + winsmain.Length)
                    {
                        Wins(msg.Substring(0, i),
                            ParseDouble(msg.Substring(i + winsmain.Length, k - i - winsmain.Length)), comb);
                        return;
                    }
                }

                i = msg.IndexOf(ties);
                if (i >= 0)
                {
                    j = msg.IndexOf(winswith);
                    string comb = null;
                    if (j >= 0)
                        comb = msg.Substring(j + winswith.Length);
                    int k = msg.IndexOf(rcp);
                    if (k >= i + ties.Length)
                    {
                        Ties(msg.Substring(0, i),
                            ParseDouble(msg.Substring(i + ties.Length, k - i - ties.Length)), comb);
                        return;
                    }
                }


                i = msg.IndexOf(sitout);
                if (i >= 0)
                {
                    SitOut(msg.Substring(0, i));
                    return;
                }

                i = msg.IndexOf(sitdown);
                if (i >= 0)
                {
                    SitDown(msg.Substring(0, i));
                    return;
                }

                i = msg.IndexOf(standup);
                if (i >= 0)
                {
                    StandUp(msg.Substring(0, i));
                    return;
                }


                i = msg.IndexOf(reconnect);
                if (i >= 0)
                {
                    Reconnect(msg.Substring(0, i));
                    return;
                }

                i = msg.IndexOf(disconnect);
                if (i >= 0)
                {
                    Disconnect(msg.Substring(0, i));
                    return;
                }

                i = msg.IndexOf(mucks);
                if (i >= 0)
                {
                    Muck(msg.Substring(0, i));
                    return;
                }


                NotParsed(msg, null);
            }
            catch (Exception e)
            {
                NotParsed(msg, e);
            }
        }

        void NotParsed(string unparsed, Exception e)
        {
            Layers.MyLogger.WriteLine("NotParsed({0}). Error {1}",unparsed, e);
        }

        void SitOut(string player)
        {
            Layers.MyLogger.WriteLine("SitOut({0})", player);
        }

        void SitDown(string player)
        {
            Layers.MyLogger.WriteLine("SitDown({0})", player);
        }

        void StandUp(string player)
        {
            Layers.MyLogger.WriteLine("StandUp({0})", player);
        }

        void Reconnect(string player)
        {
            Layers.MyLogger.WriteLine("Reconnect({0})", player);
        }
        
        void Disconnect(string player)
        {
            Layers.MyLogger.WriteLine("Disconnect({0})", player);
        }

        void Muck(string player)
        {
            Layers.MyLogger.WriteLine("Muck({0})", player);
        }

        void Wins(string player, double pot, string comb)
        {
            Layers.MyLogger.WriteLine("Wins({0},{1},{2})", player, pot, comb);
        }

        void Ties(string player, double pot, string comb)
        {
            Layers.MyLogger.WriteLine("Wins({0},{1},{2})", player, pot, comb);
        }

        void Shows(string player, string comb)
        {
            Layers.MyLogger.WriteLine("Shows({0},{1})", player, comb);
        }

        void River(string card)
        {
            CardSet river =CardSet.ParseAll(card,1);
            _target.Server.PostRiver(river);

            Layers.MyLogger.WriteLine("River({0})", river);
        }

        void Turn(string card)
        {
            CardSet turn = CardSet.ParseAll(card, 1);
            _target.Server.PostTurn(turn);

            Layers.MyLogger.WriteLine("Turn({0})", turn);
        }

        void Flop(string cards)
        {
            CardSet flop = CardSet.ParseAll(cards,3);
            if (null != _target)
                _target.Server.PostFlop(flop);

            Layers.MyLogger.WriteLine("Flop({0}) ", flop);
        }

        void Fold(string player)
        {
            if (null != _target)
            {
                
            }
            Layers.MyLogger.WriteLine("Fold({0})", player);
        }

        void Bet(string player, double amount)
        {
            Layers.MyLogger.WriteLine("Bet({0},{1})", player, amount);
        }

        void BetAdd(string player, double amount)
        {
            Layers.MyLogger.WriteLine("BetAdd({0},{1})", player, amount);
        }

        void BetReturn(string player, double amount)
        {
            Layers.MyLogger.WriteLine("BetReturn({0},{1})", player, amount);
        }

        void Call(string player, double amount)
        {
            Layers.MyLogger.WriteLine("Call({0},{1})", player, amount);
        }

        void RaiseTo(string player, double toAmount)
        {
            Layers.MyLogger.WriteLine("RaiseTo({0},{1})", player, toAmount);
        }

        void Check(string player)
        {
            Layers.MyLogger.WriteLine("Check({0})", player);
        }

        GamePlayer PlayerFromName(string MyName)
        {
            if(null!=_target)
            {
                GamePlayer p = _target.State.Players.FromName(MyName);
                return p;
            }
            return null;
        }

        void MyCards(string cards)
        {
            GamePlayer p = null;
            if (null != _target)
            {
                p = PlayerFromName(MyName);
                if(null==p)
                    throw new InvalidOperationException(string.Format("No {0} player at the table",MyName));
                //_target.Server.SetCurrent(p);
                _target.Server.PocketCards(p, CardSet.ParseAll(cards, 2));
            }
            Layers.MyLogger.WriteLine("SetCurrent({1}), MyCards({0})", cards, p);
        }

        void Blind(string player, double blind, Post kind)
        {
            Layers.MyLogger.WriteLine("Blind({0},{1},{2})", player,blind,kind);
        }

        void TimeLeft(string player, double time)
        {
            Layers.MyLogger.WriteLine("TimeLeft({0},{1})", player,time);
        }

        void HandStart(long id)
        {
            position = 0;
            position = 0;
            handId = id;
            if (null != _table)
                _table.Mosaic.Tiles.Clear();

            if (null != _target)
            {
                _target.Server.NewGame();
                step++; // now at step "0"
            }

            Layers.MyLogger.WriteLine("HandStart({0})", id);
        }
    }

    public class DriverEventArgs : EventArgs
    {
        public enum EventKind
        {
            TableConnected
        };

        public EventKind Kind;
        public FTTable Table;

        public DriverEventArgs(EventKind kind, FTTable table)
        {
            Kind = kind;
            Table = table;
        }
    }

    public class FTDriver 
    {
        static string ChannelName = null;
        static string TargetName = "FullTiltPoker";
        static string AppName = "InjectProxy.dll";

        static public WinWndMap WndMap = new WinWndMap();

        static public FTTable FTTable;

        static private EventHandler<DriverEventArgs> _drvEvent;

        static Dictionary<string, FTTable> Games = new Dictionary<string,FTTable>();

        static public event EventHandler<DriverEventArgs> DriverEvent
        {
            add { _drvEvent+=value; }
            remove { _drvEvent -= value; }
        }

        public static void Start()
        {

            InjectClient.Inject(TargetName, AppName);

            //InjectClient.InjectEvent += new EventHandler<CallEventArgs>(Instance_InjectEvent);
            InjectClient.WindowEvent += new EventHandler<WindowEventArgs>(Instance_WindowEvent);

        }

        static void NotifyWnd(WinWnd wnd)
        {
            foreach(WinWnd readyWnd in WndMap)
                readyWnd.OnWindowLinked(wnd);
            wnd.OnWindowLinked(null);
        }

        static void Instance_WindowEvent(object sender, WindowEventArgs e)
        {
            WinWnd newWnd = CreateWnd(e);
            if (null != newWnd)
            {
                WndMap.Add(newWnd);
                NotifyWnd(newWnd);
            }
        }

        static WinWnd CreateWnd(WindowEventArgs e)
        {
            if (e.Wnd.ClassName.Equals("FTCChat"))
            {
                Console.WriteLine("FOUND: FTCChat {0}", e.Wnd);
                FTChat FTChat = new FTChat(e.Wnd);
                FTChat.NewMessage += new EventHandler<StringEventArgs>(FTChat_NewMessage);
                return FTChat;
            }
            else if (e.Wnd.ClassName.Equals("FTC_TableViewFull"))
            {
                Console.WriteLine("FOUND: FTC_TableViewFull");
                FTTable FTTable = new FTTable(e.Wnd);
                return FTTable;
            }
            else
            {
                Console.WriteLine("SKIPPED: Unknown {0}", e.Wnd);
                return new WinWnd(e.Wnd);
            }
            throw new InvalidOperationException();
        }

        static public void NotifyGameOpened(FTTable src)
        {
            if(null!=_drvEvent)
                _drvEvent(src, new DriverEventArgs(DriverEventArgs.EventKind.TableConnected, src));                
        }

        static void FTChat_NewMessage(object sender, StringEventArgs e)
        {
            Console.WriteLine("\tFullTiltChat[{0}]: {1}",e.InTime, e.Message);
        } 
        
        static void Instance_InjectEvent(object sender, CallEventArgs e)
        {
            
        }
    }
}
